if class == nil then require 'class' end
require 'card'
require 'enum'
require 'util'


if Hand == nil then Hand = class({}) end

function Hand:constructor( hand )
    self.myHand = {}
    self.handValue = HAND_VALUE.HIGH
    self.highRanks = {}
    if hand ~= nil then
        for k,v in pairs( hand.myHand ) do
            self.myHand[k] = v
        end
        for k,v in pairs( hand.highRanks ) do
            self.highRanks[k] = v
        end
        self.handValue = hand.handValue
    end
end

function Hand:Clear()
    self.myHand = {}
    self.handValue = HAND_VALUE.HIGH
    self.highRanks = {}
end

function Hand:Add( card )
    table.insert(self.myHand, card)
end

function Hand:Remove( o )
    if type(o) == "number" then
        table.remove( self.myHand, o )
    elseif type(o) == "table" then
        local k = TableFindKey(o)
        table.remove( self.myHand, k)
    end
end

function Hand:GetValue()
    return self.handValue
end
function Hand:SetValue( value )
    self.handValue = value
end
function Hand:SetHighRank( rank )
    table.insert( self.highRanks, rank)
end
function Hand:GetHighRank(i)
    return self.highRanks[i]
end

function Hand:GetRankAt(i)
    return self:GetCard(i):GetRank()
end

function Hand:GetSuitAt(i)
    return self:GetCard(i):GetSuit()
end

function Hand:Count()
    return TableCount(self.myHand)
end

function Hand:GetCard(index)
    if index > self:Count() then
        error("Argument out of range exception " .. index .. " self count->", self:Count())
    end
    return self.myHand[index]
end

function Hand:SortByRank()
    self.myHand = self:QuickSortRank( self.myHand )
    return self
end
function Hand:QuickSortRank( myCards )
    if TableCount( myCards ) <= 1 then
        return myCards
    end
    local pivot = RandomInt(1, TableCount(myCards))
    local temp = table.remove(myCards, pivot)

    local less = {}
    local greater = {}
    for k,card in pairs( myCards ) do
        if card:GetRank() > temp:GetRank() then
            table.insert( greater, card )
        else
            table.insert( less, card)
        end
    end
    local list = {}
    local t_greater = Hand:QuickSortRank(greater)
    for i = 1, TableCount(t_greater) do
        table.insert( list, t_greater[i] )
    end
    table.insert( list, temp )
    local t_less = Hand:QuickSortRank(less)
    for i = 1, TableCount(t_less) do
        table.insert( list, t_less[i] )
    end
    return list
end
function Hand:SortBySuit()
    self.myHand = self:QuickSortSuit( self.myHand )
    return self
end
function Hand:QuickSortSuit( myCards )
    if TableCount( myCards ) <= 1 then
        return myCards
    end
    local pivot = RandomInt(1, TableCount(myCards))
    local temp = table.remove(myCards, pivot)

    local less = {}
    local greater = {}
    for k, card in pairs( myCards ) do
        if card:GetSuit() > temp:GetSuit() then
            table.insert( greater, card)
        else
            table.insert( less, card)
        end
    end

    local list = {}
    local list = {}
    local t_greater = Hand:QuickSortSuit(greater)
    for i = 1, TableCount(t_greater) do
        table.insert( list, t_greater[i] )
    end
    table.insert( list, temp )
    local t_less = Hand:QuickSortSuit(less)
    for i = 1, TableCount(t_less) do
        table.insert( list, t_less[i] )
    end
end

function Hand:RawToString()
    local ret = ""
    for i = 1, self:Count() do
       ret = ret .. " | " .. self.myHand[i]:ToString() 
    end
    return ret .. " | "
end

function Hand:ToString()
    if self:Count() <= 0 then
        return "No Poker in hand is found"
    end
    local rts = function(val) return Card:RankToString(val) end
    local val = self:GetValue()
    local high = function(i) return self:GetHighRank(i) end
    local case
    if val == 1 then case = high(1) .. " High" end
    if val == 2 then case = "Pair of " .. high(1) end
    if val == 3 then case = "Two Pair: " .. high(1) end
    if val == 4 then case = "Three " .. high(1) .. "s" end
    if val == 5 then case = high(1) .. " High Straight" end
    if val == 6 then case = high(1) .. " High Flush" end
    if val == 7 then case = high(1) .. "s Full of " .. high(2) .. "s" end
    if val == 8 then case = "Quad " .. high(1) .. "s" end
    if val == 9 then case = high(1) .. " High Straight Flush" end
    if val == 10 then case = "Royal Flush" end
    return case
end

function Hand:IsEqual(a)
    for k,card in pairs(a.myHand) do
        local mCard = self.myHand[k]
        if (mCard:GetSuit() ~= card:GetSuit() or mCard:GetRank() ~= card:GetRank()) then
            return false
        end
    end
    return true
end
